/************************************************************
* @file   gkt_rtc.c
************************************************************/

#include "gkt_board.h"
#include "gkt_debug.h"
#include "rtc_defines.h"
#include "rtc_regs.h"
#include "gkt_rtc.h"
#include "gkt_wakeup.h"
#include "gkt_gpio.h"
#include "gkt_os.h"
#include "gkt_version.h"

//RTC_ATTRS_DEFINE;

#ifdef GKT_CONFIG_CLOCK_RTC_SOURCE
#define RTC_CLOCK_SOURCE_DEFAULT GKT_CONFIG_CLOCK_RTC_SOURCE
#else
#error "no defined GKT_CONFIG_RTC_CLOCK_SOURCE, please comfirm!!!"
#endif

#define RTC_RC_SOURCE_TYPE MRTCLSE

#ifdef GKT_RTC_CALENDAR_ACTIVE_DELAY_S
#define RTC_CALENDAR_ACTIVE_DELAY_S	GKT_RTC_CALENDAR_ACTIVE_DELAY_S
#else
#define RTC_CALENDAR_ACTIVE_DELAY_S	2
#endif
#define RTC_CALENDAR_ACTIVE_DELAY_US	(RTC_CALENDAR_ACTIVE_DELAY_S * 1000000)

/* rtc state */
#define RTC_ST_CALENDAR_SW_CONFIG	(1U << 0)	/* sw configured, maybe not active yet */
#define RTC_ST_CALENDAR_HW_ACTIVE	(1U << 1)	/* hw active, calendar is in updating */

#define RTC_ST_ALARM_SW_ENABLE	(1U << 8)	/* alarm is enabled */
#define RTC_ST_ALARM_HW_ACTIVE	(1U << 9)	/* system is alarm by rtc */

#define RTC_WAIT_COMPLET_DELAY_MS 50
typedef struct _rtc {
	uint32_t	state;

	uint32_t	timestamp_of_set;
	gkt_date_time_s	dt_of_set;

	int	wakedup;
	gkt_rtc_alarm_fn	callback;
	int8_t RTCLSE_ClkSource;
	int8_t rtc_init;

} rtc_s;

static __DEVINFO_BSS rtc_s s_rtc;
static const gkt_date_time_s sc_dt_base =
		GKT_DATE_TIME(GKT_WINDOW_DATE_YEAR_DEFAULT, GKT_DT_MONTH_JAN, 1, 0, 0, 0);
int gkt_rtc_set_time(gkt_date_time_s *date_time)
{
	uint32_t dt_regval;
	int retval;

	gkt_trace("gkt_rtc_set_time: %d-%d-%d_%d:%d:%d.\n", date_time->date.year, date_time->date.month,
			date_time->date.day, date_time->time.hour,
			date_time->time.minute, date_time->time.second);
	if (!(s_rtc.state & RTC_ST_CALENDAR_SW_CONFIG) || !s_rtc.rtc_init)
		return GKT_ENOTINIT;

	if (date_time 
		&& (date_time->date.year >= RTC_YEAR_BASE)
		&& (date_time->date.year < RTC_YEAR_END)
		&& gkt_dt_check_date(&date_time->date)
		&& gkt_dt_check_time(&date_time->time))
	{
		retval = gkt_dt_seconds_elapsed_from_base(date_time, &sc_dt_base, &dt_regval);
		if (GKT_SUCCESS == retval) {
			s_rtc.state &= ~RTC_ST_CALENDAR_HW_ACTIVE;
			G_RTC_SetRefRegister(RTC_RC_SOURCE_TYPE, dt_regval);
			s_rtc.state |= RTC_ST_CALENDAR_SW_CONFIG;
			gkt_thread_sleep(RTC_WAIT_COMPLET_DELAY_MS);
			return GKT_SUCCESS;
		}
	}
	return GKT_EPARAM;
}


static void rtc_get_time(gkt_date_time_s *date_time)
{
	uint32_t dt_regval;

	if (!s_rtc.rtc_init)
		return;

	dt_regval = G_RTC_GetRefRegister(RTC_RC_SOURCE_TYPE);
	date_time->date.u32_value = sc_dt_base.date.u32_value;
	date_time->time.u32_value = sc_dt_base.time.u32_value;

	gkt_dt_add_seconds(date_time, dt_regval);
}

int gkt_rtc_get_time(gkt_date_time_s *date_time)
{
	int retval;

	if (!s_rtc.rtc_init)
		return GKT_ENOTINIT;

	if (date_time) {
		retval = GKT_SUCCESS;

		if (s_rtc.state & RTC_ST_CALENDAR_HW_ACTIVE)
			rtc_get_time(date_time);
		else if (s_rtc.state & RTC_ST_CALENDAR_SW_CONFIG) {
				s_rtc.state |= RTC_ST_CALENDAR_HW_ACTIVE;
				s_rtc.timestamp_of_set = 0;
				rtc_get_time(date_time);
		}
		else
			retval = GKT_ENOTCONFIG;	/* not set/config date_time yet */
	}
	else
		retval = GKT_EPARAM;

	gkt_trace("gkt_rtc_get_time retval=%d: %d-%d-%d_%d:%d:%d.\n", retval, date_time->date.year, date_time->date.month,
			date_time->date.day, date_time->time.hour, 
			date_time->time.minute, date_time->time.second);
	return retval;
}

static void rtc_clear_alarm(void)
{
	if (!(s_rtc.state & RTC_ST_CALENDAR_SW_CONFIG)|| !s_rtc.rtc_init)
		return;

	uint32_t alarm_value_sec = 0;
	G_BPK_WriteKey(&alarm_value_sec, 1, GKT_RETMEM_RTC_ALARM);

	s_rtc.state &= ~RTC_ST_ALARM_SW_ENABLE;
	G_RTC_ITConfig(RTC_RC_SOURCE_TYPE, RTC_ALARM_IRQ, DISABLE);
    G_RTC_ClearITPendingBit(RTC_RC_SOURCE_TYPE, RTC_ALL_IRQ);
	NVIC_ClearPendingIRQ(RTC_IRQn);
}

int gkt_rtc_set_alarm(uint64_t time_us, int periodic)
{
	gkt_unused(periodic);
	uint32_t alarm_value_sec;

	gkt_info("rtc_set_alarm: state(0x%x), time(%uus)\n",
		s_rtc.state, time_us);

	if (!(s_rtc.state & RTC_ST_CALENDAR_SW_CONFIG) || !s_rtc.rtc_init)
		return GKT_ENOTINIT;

	if (s_rtc.state & RTC_ST_ALARM_SW_ENABLE)
		rtc_clear_alarm();

	alarm_value_sec = G_RTC_GetCounter(RTC_RC_SOURCE_TYPE) + time_us/(1000*1000);
	G_BPK_WriteKey(&alarm_value_sec, 1, GKT_RETMEM_RTC_ALARM);
	G_RTC_SetAlarm(RTC_RC_SOURCE_TYPE, alarm_value_sec);
    G_RTC_ITConfig(RTC_RC_SOURCE_TYPE, RTC_ALARM_IRQ, ENABLE);
    G_RTC_ClearITPendingBit(RTC_RC_SOURCE_TYPE, RTC_ALARM_IRQ);
	gkt_thread_sleep(RTC_WAIT_COMPLET_DELAY_MS);

	s_rtc.state |= RTC_ST_ALARM_SW_ENABLE;

	return GKT_SUCCESS;

}

void gkt_rtc_clear_alarm(void)
{
	if (!s_rtc.rtc_init)
		return;

 	if (s_rtc.state & RTC_ST_ALARM_SW_ENABLE) {
		rtc_clear_alarm();
 	}
}
int gkt_rtc_detect_alarm(void)
{
	uint32_t alarm_value_sec, cnt;
	int retval;

	if (!s_rtc.rtc_init)
		return GKT_ENOTINIT;

	G_BPK_ReadKey(&alarm_value_sec, 1, GKT_RETMEM_RTC_ALARM);
	if (alarm_value_sec == 0)
		return GKT_ENOTEXIST;

	cnt = G_RTC_GetCounter(RTC_RC_SOURCE_TYPE);
	gkt_printf("detect_alarm: alarm_value=%u, cnt=%u\n", alarm_value_sec, cnt);
	if (cnt >= alarm_value_sec)
		retval = GKT_SUCCESS;
	else
		retval = GKT_EFATAL;

	alarm_value_sec = 0;
	G_BPK_WriteKey(&alarm_value_sec, 1, GKT_RETMEM_RTC_ALARM);
	return retval;
}

void gkt_rtc_register_alarm_callback(gkt_rtc_alarm_fn callback)
{
	s_rtc.callback = callback;
}

void gkt_rtc_irq_handler(void)
{
	G_RTC_IRQ_TypeDef irq;
    memset(&irq, 0, sizeof(irq));

	if (!s_rtc.rtc_init)
		return;

    irq = G_RTC_GetITStatus();
    if (RESET != irq.rtclsi_irq) {
		G_RTC_ClearITPendingBit(MRTCLSI, RTC_ALL_IRQ);
        if (RESET != irq.rtclsi_sec_irq)
            gkt_info("RTC_LSI_IE_IRQ to rtclsi_sec_irq = %u!\r\n", G_RTC_GetCounter(MRTCLSI));
        if (RESET != irq.rtclsi_alarm_irq) {
			gkt_info("RTC_LSI_IE_IRQ to rtclsi_alarm_irq = %u!\r\n", G_RTC_GetCounter(MRTCLSI));
			if (GKT_SYSTEM_RESET_WAKEUP == g_system_info.reset_mode) {
				s_rtc.wakedup = 1;
				gkt_wakeup_set_source(GKT_WAKEUP_SRC_RTC);
			}
			if (s_rtc.callback)
				(*s_rtc.callback)();
        }
    }
    if (RESET != irq.rtclse_irq) {
		G_RTC_ClearITPendingBit(MRTCLSE, RTC_ALL_IRQ);
        if (RESET != irq.rtclse_sec_irq)
            gkt_info("RTC_LSE_IE_IRQ to rtclse_sec_irq = %d!\r\n", G_RTC_GetCounter(MRTCLSE));
        if (RESET != irq.rtclse_alarm_irq) {
			gkt_info("RTC_LSE_IE_IRQ to rtclse_alarm_irq = %u!\r\n", G_RTC_GetCounter(MRTCLSE));
			if (GKT_SYSTEM_RESET_WAKEUP == g_system_info.reset_mode) {
				s_rtc.wakedup = 1;
				gkt_wakeup_set_source(GKT_WAKEUP_SRC_RTC);
			}
			if (s_rtc.callback)
				(*s_rtc.callback)();
        }
    }

    //G_RTC_ResetCounter(MRTCLSI);
    NVIC_ClearPendingIRQ(RTC_IRQn);
}

int gkt_rtc_is_wakeup(void)
{
	return s_rtc.wakedup;
}
int gkt_rtc_get_rc_status(void)
{
	if (s_rtc.RTCLSE_ClkSource != RTCLSE_CLKSOURCE_LSE32K &&
		s_rtc.RTCLSE_ClkSource != RTCLSE_CLKSOURCE_LSI32K)
		return GKT_ENOTEXIST;
	return GKT_SUCCESS;
}

#ifdef GKT_CONFIG_RTC_DETECT_RC_SOURCE_PIN
#define DETECT_RC_SOURCE_PIN GKT_CONFIG_RTC_DETECT_RC_SOURCE_PIN
#else
#error "no define GKT_CONFIG_RTC_DETECT_RC_SOURCE_PIN"
#endif
#define RC_SOURCE_F_MRTCLSE (1U << 0)
#define RC_SOURCE_F_MRTCLSI (1U << 1)
#define RC_READ_GPIO_XTAL32K_INTERVAL_TIME_US 5
#define RC_READ_GPIO_XTAL32K_BIT_FLIPS_COUNT_MIN 10
#define RC_READ_GPIO_XTAL32K_TEST 0
#define RC_READ_GPIO_XTAL32K_COUNT 100
#define RC_READ_GPIO_XTAL32K_TIME_OUT_MS 1000

static uint8_t rtc_detect_rc_get_flip_count(void)
{
	uint32_t port_id, pin_id, gpio_pin;
	uint8_t  io_bit, io_bit_next, bit_flips_count;
	uint32_t time_out_count;

	port_id = GKT_GPIO_PORTID(GKT_CONFIG_RTC_DETECT_PIN);
	pin_id = GKT_GPIO_PINID(GKT_CONFIG_RTC_DETECT_PIN);
	gpio_pin = pin_id+(port_id*16);

	gkt_printf("det rc start\n");
	gkt_trace("reset mode = %d, port_id:%d, pin_id=%d\n", 
		g_system_info.reset_mode, port_id, pin_id);

	GPIO_CONFIG(gpio_pin) = XTAL32K;
#if RC_READ_GPIO_XTAL32K_TEST
	uint32_t i = 0, j = 0;
	uint8_t io_bit_array[RC_READ_GPIO_XTAL32K_COUNT*2];
	uint32_t gpio_addr = 0xf8700 + gpio_pin;
	gkt_printf("0xfaa30 reg = 0x%x, GPIO%d addr = 0x%x GPIO_FUNC = %02x\n", 
		*(volatile uint8_t*)(0xfaa30), gpio_pin, gpio_addr, *(volatile uint8_t*)(gpio_addr));
#endif

	uint32_t start_time = gkt_systime_ms();
	while (1) {
		time_out_count = RC_READ_GPIO_XTAL32K_COUNT;
		bit_flips_count = 0;
#if RC_READ_GPIO_XTAL32K_TEST
		i = 0;
#endif
		while (time_out_count) {
			gkt_delay_us(RC_READ_GPIO_XTAL32K_INTERVAL_TIME_US);
			io_bit = G_GPIO_ReadInputDataBit(port_id, DETECT_RC_SOURCE_PIN);
#if RC_READ_GPIO_XTAL32K_TEST
			io_bit_array[i++] = io_bit;
#endif
			gkt_delay_us(RC_READ_GPIO_XTAL32K_INTERVAL_TIME_US);
			io_bit_next = G_GPIO_ReadInputDataBit(port_id, DETECT_RC_SOURCE_PIN);
#if RC_READ_GPIO_XTAL32K_TEST
			io_bit_array[i++] = io_bit_next;
#endif
			if (io_bit_next != io_bit) {
				bit_flips_count++;
				if (bit_flips_count >= RC_READ_GPIO_XTAL32K_BIT_FLIPS_COUNT_MIN) {
					goto GOTO_END;
				}
			}
			time_out_count--;
		}
		gkt_delay_ms(10);
		if ((gkt_systime_ms() - start_time) >= RC_READ_GPIO_XTAL32K_TIME_OUT_MS)
			break;
	}
GOTO_END:
	GPIO_CONFIG(gpio_pin) = PULL_PD;
#if RC_READ_GPIO_XTAL32K_TEST
	gkt_printf("IO:\n");
	for (j = 0; j<i; j++) {
		gkt_printf("%d ", io_bit_array[j]);
		if (j%20 == 0 && (j != 0))
			gkt_printf("\n");
	}
	gkt_printf("\n");
	gkt_printf("flips_count=%d, time_out_count=%d\n", bit_flips_count, time_out_count);
#endif

	gkt_printf("detect_rc need time is %dms\n", gkt_systime_ms() - start_time);
	return bit_flips_count;
}
#if 0
static void rtc_select_rc_source_lsi32k(int is_config)
{
	s_rtc.RTCLSE_ClkSource = RTCLSE_CLKSOURCE_LSI32K;
	G_BPK_WriteKey((uint32_t*)&s_rtc.RTCLSE_ClkSource, 1, GKT_RETMEM_RTC_RC_SOURCE);
	if (is_config) {
		G_RTC_LseClkConfig(RTCLSE_CLKSOURCE_LSI32K, ENABLE);
		gkt_thread_sleep(RTC_WAIT_COMPLET_DELAY_MS);
		gkt_printf("RTC CLKSOURCE CONFIG LSI32K, 0xfaa30 reg=%d\n", *(volatile uint8_t*)(0xfaa30));
	}
}
#endif
static int rtc_detect_rc_source(void)
{
	uint8_t bit_flips_count;

	if ((g_system_info.reset_mode == GKT_SYSTEM_RESET_WAKEUP) ||
		(g_system_info.reset_mode == GKT_SYSTEM_RESET_SOFT)) 
	{
		bit_flips_count = rtc_detect_rc_get_flip_count();
		if (bit_flips_count < RC_READ_GPIO_XTAL32K_BIT_FLIPS_COUNT_MIN) {
			gkt_error("rtc clk source exception\n");
			return GKT_ENOTEXIST;
		}
	}
	else {
		if (RTC_CLOCK_SOURCE_DEFAULT == RTCLSE_CLKSOURCE_LSE32K) {
			G_RTC_LseClkConfig(RTCLSE_CLKSOURCE_LSE32K, ENABLE);
			s_rtc.RTCLSE_ClkSource = RTCLSE_CLKSOURCE_LSE32K;
		}
		else {
			G_RTC_LseClkConfig(RTCLSE_CLKSOURCE_LSI32K, ENABLE);
			s_rtc.RTCLSE_ClkSource = RTCLSE_CLKSOURCE_LSI32K;
		}
		//*(volatile uint8_t *)(0xfaf00) = 0;

		gkt_printf("RTC CLKSOURCE CONFIG, 0xfaa30 reg=%d (lsi - 1, lse - 3\n", *(volatile uint8_t*)(0xfaa30));
		//gkt_thread_sleep(RTC_CALENDAR_ACTIVE_DELAY_S*1000);
		bit_flips_count = rtc_detect_rc_get_flip_count();
		if (bit_flips_count < RC_READ_GPIO_XTAL32K_BIT_FLIPS_COUNT_MIN) {
			gkt_printf("rtc clk source does not exist\n");
			return GKT_ENOTEXIST;
		}
	}
	return GKT_SUCCESS;
}
int gkt_rtc_init(void)
{
	gkt_irq_attr_s irq_attr;
	int retval = GKT_SUCCESS;
	uint32_t alarm_value_sec;

	s_rtc.rtc_init = 0;

	if (rtc_detect_rc_source() != GKT_SUCCESS) {
		s_rtc.RTCLSE_ClkSource = -1;
		return GKT_ENOTINIT;
	}
	gkt_info("rc_source config complete, start run rtc_init...\n");
	if (G_RTC_Init(RTC_RC_SOURCE_TYPE))
		s_rtc.state |= RTC_ST_CALENDAR_HW_ACTIVE | RTC_ST_CALENDAR_SW_CONFIG;

	if (GKT_SYSTEM_RESET_WAKEUP != g_system_info.reset_mode) {
		G_RTC_ResetCounter(RTC_RC_SOURCE_TYPE);
		G_RTC_SetRefRegister(RTC_RC_SOURCE_TYPE, 0);
		gkt_trace("GET RTC Ref Register value: %u \r\n", G_RTC_GetCounter(RTC_RC_SOURCE_TYPE));
		//G_RTC_ITConfig(RTC_RC_SOURCE_TYPE, RTC_ALARM_IRQ, ENABLE);
		alarm_value_sec = 0;
		G_BPK_WriteKey(&alarm_value_sec, 1, GKT_RETMEM_RTC_ALARM);
	}

	/* setup rtc irq */
	irq_attr.flags = GKT_IRQ_F_ENABLE | GKT_IRQ_F_CLEAR | GKT_IRQ_F_UNMASK;
	irq_attr.priority = GKT_IRQ_PRIORITY(GKT_RTC_IRQ_PRIORITY);
	irq_attr.handler = gkt_rtc_irq_handler;
	retval = gkt_irq_setup(GKT_RTC_IRQ_NO, &irq_attr);
	if (__UNLIKELY(retval != GKT_SUCCESS))
		return retval;

	s_rtc.rtc_init = 1;
	rtc_clear_alarm();

	gkt_trace("rtc_init: state = 0x%x\n", s_rtc.state);
	return GKT_SUCCESS;
}

GKT_DEVICE_DESC_DEFINE(rtc, BASE);

#define RTC_LP_CHECK_TIMEOUT	((RTC_CALENDAR_ACTIVE_DELAY_S+1) * 1000)
#define RTC_LP_CHECK_TIME_UNIT	10
#define RTC_LP_CHECK_LOOPS	\
	(RTC_LP_CHECK_TIMEOUT / RTC_LP_CHECK_TIME_UNIT)
void gkt_rtc_lp_check(void)
{
	if (RTC_CLOCK_SOURCE_DEFAULT == RTCLSE_CLKSOURCE_LSI32K)
		G_RTC_Calibration();
#if 0
	uint32_t loops = RTC_LP_CHECK_LOOPS;

	do {
		if (!rtc_dt_active_pending(GKT_RTC_BASEADDR))
			break;
		else if (!loops) {
			gkt_error("rtc_lp_check: active pending - timeout!\n");
			break;
		}

		gkt_delay_ms(RTC_LP_CHECK_TIME_UNIT);
		loops--;
	} while(1);
#endif
}

